﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace PluginPathFinder
{
    public static class PathFinderExtensions
    {
        public static void FindShortestPathOfPoints(this PathFinder manager,int startNodeID,int endNodeID,PathLineType pathType,System.Action<List<Vector3>> OnPathFound)
        {
            PathFinderUtility.FindShortestPathOfPoints_Internal(manager, startNodeID, endNodeID, pathType, OnPathFound);
        }

        public static void FindShortestPathOfPoints(this PathFinder manager,Vector3 startPoint,Vector3 endPoint,PathLineType pathType,System.Action<List<Vector3>> OnPathFound)
        {
            PathFinderUtility.FindShortestPathOfPoints_Internal(manager, startPoint, endPoint,pathType, OnPathFound);
        }
    }


    public static class PathFinderUtility
    {
        public static PathFollower FollowPathWithGroundSnap(Transform transform,List<Vector3> points
                                                            ,float moveSpeed
                                                            ,bool  autoRotateToDestion
                                                            ,Vector3 directionRayCast
                                                            ,float offsetDistanceToFloatFromGround
                                                            , int groundGameObjectLayer
                                                            , float offsetDistanceFromPoint = 10
                                                            , int maxDistanceForRayCast = 40
                                                            )
        {
            var pathFollower = CreateWithSnapToGround(transform, directionRayCast, offsetDistanceFromPoint, offsetDistanceToFloatFromGround, maxDistanceForRayCast, groundGameObjectLayer);
            if(points != null)
            {
                pathFollower.Follow(points,moveSpeed,autoRotateToDestion);
            }
            return pathFollower;
        }

        private static PathFollower CreateWithSnapToGround(Transform transform, Vector3 directionOfRayCast, 
                                                           float offsetDistanceFromPoint, float offsetDistanceToFloatFromGround, 
                                                           int maxDistanceForRayCast, int groundLayer)               
        {

            /*
            var pathFollower = transform.GetComponent<PathFollowerToPoint>();
            if(pathFollower == null)
            {
                pathFollower = transform.gameObject.AddComponent<PathFollowerToPoint>();
            }

            pathFollower.SetContext(directionOfRayCast,offsetDistanceFromPoint,offsetDistanceToFloatFromGround,maxDistanceForRayCast,groundLayer);
            pathFollower._transform = transform;

            return pathFollower;
            */
            var pathFollower = transform.GetComponent<PathFollowToPositionAndSnapToGround>();
            if (pathFollower == null)
            {
                pathFollower = transform.gameObject.AddComponent<PathFollowToPositionAndSnapToGround>();
            }

            pathFollower.SetContext(directionOfRayCast, offsetDistanceFromPoint, offsetDistanceToFloatFromGround, maxDistanceForRayCast, groundLayer);
            pathFollower._transform = transform;

            return pathFollower;
        }

        public static void Stop(Transform transform)
        {
            var pathFollower = transform.GetComponent<PathFollower>();
            if(pathFollower)
            {
                pathFollower.StopFollowing();
                GameObject.DestroyImmediate(pathFollower);
            }
        }

        internal static void FindShortestPathOfPoints_Internal(PathFinder manager,int startNodeID,int endNodeID,PathLineType pathType,System.Action<List<Vector3>> OnPathFound)
        {
            int nearestPointFromStart = startNodeID;
            int nearestEndFromEnd = endNodeID;

            if(nearestEndFromEnd < 0 || nearestPointFromStart < 0)
            {
                OnPathFound(null);
                return;
            }

            float startTime = Time.realtimeSinceStartup;

            System.Action<List<Node>> onPathOfNodeFound = delegate (List<Node> nodes)
            {
                if (nodes == null || nodes.Count == 0)
                    OnPathFound(null);

                List<System.Object> allNodes = new List<System.Object>();
                List<Vector3> path = null;


                foreach (var a in nodes)
                {
                    allNodes.Add(a.Position);
                }


                path = GetStraightPathPoints(allNodes);

                OnPathFound(path);
            };

            manager.FindShortestPathOfPoints(nearestPointFromStart, nearestEndFromEnd,onPathOfNodeFound);
        }


        internal static void FindShortestPathOfPoints_Internal(PathFinder manager, Vector3 startPoint, Vector3 endPoint, PathLineType pathType, System.Action<List<Vector3>> OnPathFound)
        {
            int nearestPointFromStart = manager.FindNearestNode(startPoint);
            int nearestPointFromEnd = manager.FindNearestNode(endPoint);

            FindShortestPathOfPoints_Internal(manager, nearestPointFromStart, nearestPointFromEnd, pathType, OnPathFound);
        }

        internal static List<Vector3> GetStraightPathPoints(List<System.Object> nodePoints)
        {
            if (nodePoints == null) return null;

            List<Vector3> path = new List<Vector3>();
            if(nodePoints.Count < 2)
            {
                return null;
            }

            for (int i=0;i<nodePoints.Count;i++)
            {
                path.Add(GetPositionFromNodeOrVector(nodePoints, i));
            }

            return path;
        }


        static System.Func<List<System.Object>, int, Vector3> GetPositionFromNodeOrVector = delegate (List<System.Object> list, int index) 
        { 
            return (list[index] is Vector3 ? (Vector3)list[index] : ((Node)list[index]).Position);
        };
        static System.Func<List<System.Object>, int, Node> GetNodeFromNodeOrVector = delegate (List<System.Object> list, int index) 
        { 
            return (list[index] is Node ? (Node)list[index] : null); 
        };

        
    }
}
